package dao

import (
	"context"
	"database/sql"
	"errors"
	"gitee.com/DonHz/basic-go/webook/internal/domain"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"time"
)

var (
	ErrUserDuplicate = errors.New("邮箱或手机号码冲突")
	ErrUserNotFound  = gorm.ErrRecordNotFound
)

type UserDAO interface {
	Insert(ctx context.Context, u User) error
	FindByEmail(ctx context.Context, email string) (User, error)
	FindByPhone(ctx context.Context, phone string) (User, error)
	FindById(ctx context.Context, id int64) (User, error)
	FindByWechat(ctx context.Context, wechatInfo domain.WechatInfo) (User, error)
	UpdateById(ctx context.Context, user User) error
}

type GormUserDAO struct {
	db *gorm.DB
}

func NewUserDAO(db *gorm.DB) UserDAO {
	return &GormUserDAO{db: db}
}

func (dao *GormUserDAO) Insert(ctx context.Context, u User) error {
	// 存毫秒数
	now := time.Now().UnixMilli()
	u.CreateTime = now
	u.UpdateTime = now
	// 保持链路一致
	err := dao.db.WithContext(ctx).Create(&u).Error
	if mysqlErr, ok := err.(*mysql.MySQLError); ok {
		const uniqueConflictsErrNo uint16 = 1062
		if mysqlErr.Number == uniqueConflictsErrNo {
			// 邮箱冲突 or 手机号码冲突
			return ErrUserDuplicate
		}
	}
	return err
}

func (dao *GormUserDAO) FindByEmail(ctx context.Context, email string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("email=?", email).First(&u).Error
	//err := dao.db.WithContext(ctx).First(&u, "email=?", email).Error
	return u, err
}

func (dao *GormUserDAO) FindByPhone(ctx context.Context, phone string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("phone=?", phone).First(&u).Error
	return u, err
}

func (dao *GormUserDAO) FindById(ctx context.Context, id int64) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("id=?", id).First(&u).Error
	//err := dao.db.WithContext(ctx).First(&u, "email=?", email).Error
	return u, err
}
func (dao *GormUserDAO) FindByWechat(ctx context.Context, wechatInfo domain.WechatInfo) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("wechat_open_id=?", wechatInfo.OpenID).First(&u).Error
	if err!=nil {
		err = dao.db.WithContext(ctx).Where("wechat_union_id=?", wechatInfo.UnionID).First(&u).Error
	}
	//err := dao.db.WithContext(ctx).First(&u, "email=?", email).Error
	return u, err
}

func (dao *GormUserDAO) UpdateById(ctx context.Context, user User) error {
	return dao.db.WithContext(ctx).Model(&user).Where("id = ?", user.Id).
		Updates(map[string]any{
			"nickname": user.Nickname,
			"birthday": user.Birthday,
			"about_me": user.AboutMe,
		}).Error
}

// dao中的User对应数据库的表
type User struct {
	Id       int64  `gorm:"primaryKey, autoIncrement"`
	Email    sql.NullString `gorm:"unique"`
	Password string

	// 如果通过邮箱注册，可能没有填手机号，空字符串会冲突，但可以为空
	// 使用可以为空的字符串，唯一索引可以又多个空值，但不能有空字符串
	Phone    sql.NullString `gorm:"unique"`

	Nickname string `gorm:"varchar(128)"`
	Birthday int64
	AboutMe  string `gorm:"varchar(4096)"`

	// 微信字段
	WechatUnionID sql.NullString `gorm:"unique"`
	WechatOpenID sql.NullString	`gorm:"unique"`

	// 创建时间，单位：毫秒 ms
	CreateTime int64
	// 更新时间
	UpdateTime int64
}

type Address struct {
	Id     int64
	UserId int64
}
